/*
XPTracker - Alternative Agile Tracking Tool
Copyright (C) 2006-2009  Stephen Starkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package org.xptracker.search;

import com.google.inject.Inject;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.xptracker.util.ThreadLocalCleaner;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class LuceneIndexer implements StoryIndexer {
  private static final ThreadLocal<Set<String>> deletes =
      ThreadLocalCleaner.registerForCleanup(new ThreadLocal<Set<String>>() {
        @Override
        protected Set<String> initialValue() {
          return new HashSet<String>();
        }
      });
  
  private static final ThreadLocal<Map<String, Document>> adds =
      ThreadLocalCleaner.registerForCleanup(new ThreadLocal<Map<String, Document>>() {
        @Override
        protected Map<String, Document> initialValue() {
          return new HashMap<String, Document>();
        }
      });
  private LuceneServices services;
  private IndexingHelper indexingHelper;

  @Inject
  public LuceneIndexer(final LuceneServices services, final IndexingHelper indexingHelper) {
    super();
    this.services = services;
    this.indexingHelper = indexingHelper;
  }

  public void add(final Object s) {
    try {
      adds.get().put(indexingHelper.getId(s), indexingHelper.buildDocument(s));
    } catch (final Throwable t) {
      LogFactory.getLog(getClass()).error("Error indexing item " + s, t);
    }
  }

  public void delete(Object o) {
    String id = indexingHelper.getId(o);
    if (id != null) {
      deletes.get().add(indexingHelper.getIdField(o.getClass()) + ":" + id);
    }
  }

  public void update(Object o) {
    delete(o);
    add(o);
  }

  public void commit() {
    try {
      services.withReader(new IndexReadHandler() {
        public void handleReader(IndexReader reader) throws IOException {
          for (String delete : deletes.get()) {
            String[] chunks = delete.split(":");
            reader.deleteDocuments(new Term(chunks[0], chunks[1]));
          }
        }
      });

      services.withWriter(new IndexWriteHandler() {
        public void handleWriter(IndexWriter writer) throws IOException {
          for (Document doc : adds.get().values()) {
            writer.addDocument(doc);
          }
        }
      });
    } catch (IOException e) {
      LogFactory.getLog(getClass()).error(
          "Unable to commit search index transaction.  Deletes: " + deletes.get() + ", Adds: " + adds.get(), e);
    } finally {
      cleanup();
    }
  }

  public void cleanup() {
    deletes.remove();
    adds.remove();
  }

  public void clearIndex() throws IOException {
    services.newIndex();
  }

  public void optimize() throws IOException {
    services.optimize();
  }
}
